#ifndef KEY_H
#define KEY_H

#include <functional>
#include <ostream>
#include <cstdint>

namespace RealScan
{

// Represents a 2D table index key
struct Key
{
    std::int32_t x; // x-coordinate, row 
    std::int32_t y; // y-coordinate, col

    // Parameterized constructor
    Key(std::int32_t _x, std::int32_t _y) : x(_x), y(_y) {}

    // Copy constructor
    Key(const Key& other) : x(other.x), y(other.y) {}

    // Assignment operator
    Key& operator=(const Key& other)
    {
        if (this != &other) { // Self-assignment check
            x = other.x;
            y = other.y;
        }
        return *this;
    }

    // Destructor
    ~Key() = default;

    // Equality operator (compatible with STL, like `std::unordered_map`)
    bool operator==(const Key& other) const
    {
        return (x == other.x) && (y == other.y);
    }

    // Inequality operator (compatible with STL, like `std::set`)
    bool operator<(const Key& other) const {
        return (x < other.x) && (y < other.y);
    }
};

} /* RealScan */

namespace std
{

// Hash table lengths for custom hash algorithms (see `hashmap.h`)
#define REALSCAN_HASH_TABLE_SIZE (5000 * 5000)

// Overload for key-value operation, like `unordered_map`
template<>
struct hash<RealScan::Key> 
{
    size_t operator()(const RealScan::Key& key) const 
    {
        return ((73856093 * key.x) ^ (19349663 * key.y)) % REALSCAN_HASH_TABLE_SIZE;
    }
};

// Overload for iostream
ostream& operator<<(ostream& os, const RealScan::Key& key)
{
    os << "(" << key.x << "," << key.y << ")";
    return os;
} 

} /* std */

#endif /* KEY_H */